/*
 * $Id: StandaloneMain.java 1259 2007-01-09 14:23:47Z tcoupaye $
 *
 * Behavior Protocols extensions for static and runtime checking
 * developed for the Julia implementation of Fractal.
 * 
 * Copyright 2004
 *    Distributed Systems Research Group
 *    Department of Software Engineering
 *    Faculty of Mathematics and Physics
 *    Charles University, Prague
 *
 * Copyright (C) 2006
 *    Formal Methods In Software Engineering Group
 *    Institute of Computer Science
 *    Academy of Sciences of the Czech Republic
 *
 * Copyright (C) 2006 France Telecom
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 * Contact: ft@nenya.ms.mff.cuni.cz
 * Authors: Jan Kofron <kofron@nenya.ms.mff.cuni.cz>
 *
 */

package org.objectweb.fractal.bpc.checker;

import java.io.IOException;
import java.util.ArrayList;

import org.objectweb.fractal.bpc.checker.DFSR.Options;
import org.objectweb.fractal.bpc.checker.backend.ConsentBackend;
import org.objectweb.fractal.bpc.checker.backend.DotVisBackend;
import org.objectweb.fractal.bpc.checker.node.*;
import org.objectweb.fractal.bpc.checker.parser.*;


/**
 * This class is serves for standalone use of the checker. It accepts the
 * protocols, provisions and other information from command line....
 */
public class StandaloneMain {

    /**
     * @param args
     *            commandline arguments
     */
    public static void main(String[] args) {

        // command-line data
        String[] cldata = Options.parseCommandLine(args);

        if (((cldata == null) || (cldata.length < 4)) && (Options.inputfile.equals("")))
            printUsageAndExit();


        try {
            if ((Options.action == Options.ACTIONTESTCOMPLIANCE) || (Options.action == Options.ACTIONTESTCONSENT)) {

                long ms = System.currentTimeMillis();

                ActionRepository repository = new ActionRepository();

                Object[] parsed = buildTrees(cldata, repository);
                TreeNode[] nodes = (TreeNode[])parsed[0];
                String[] provs = (String[])parsed[1];
                String unbound = (String)parsed[2];

                String result;

                result = new ConsentBackend(repository).perform(nodes, provs, unbound.equals("") ? new String[0] : unbound.split(",")).toString();

                if (result != null)
                    System.out.println(result);
                else
                    System.out.println("OK");

                Debug.println("Taken " + (System.currentTimeMillis() - ms) / 1000 + " seconds.");

            } else { // visualization
                ActionRepository repository = new ActionRepository();

                Object[] parsed = buildTrees(cldata, repository);
                TreeNode[] nodes = (TreeNode[])parsed[0];
                String[] provs = (String[])parsed[1];
                String unbound = (String)parsed[2];


                DotVisBackend visb = new DotVisBackend(repository);

                //in visualization, missing required bindings are not tested,
                // passing an empty array
                CheckingResult cr = visb.perform(nodes, provs, unbound.equals("") ? new String[0] : unbound.split(",")); 
                String result = cr ==  null ? null : cr.toString();
                if (result != null)
                    System.out.println(result);

                System.out.println("Visualization completed!");
            }
        } catch (SyntaxErrorException e) {
            System.out.println("Syntax error exception: " + e.getMessage());
        }
    }

    /**
     * Prints usage and exits.
     *
     */
    private static void printUsageAndExit() {
        System.out.println("Syntax:\njava -jar checker.jar --action=<test|testconsent|visualizedot> [--verbose|-v=<0|1|2>] [--nomultinodes|-m] [--noexplicit|-e] [--noforwardcut|-f] [--nobadactivity|-b] [--nonpactivity|-n] [--infiniteactivity|-i=<yes|no|notrace>] [(--totalmem|-t)=<size>] [(--cachesize|-s)=<size>] <((--file|-f)=<inputfile>) | (<protocol1> <provisions_1_2> <protocol2> [<provisions_2_3> <protocol3>, ...] <unbound_operations)>");
        System.exit(1);
    }

    /**
     * Erases all whitespaces from the given string.
     * 
     * @return String without whitespaces
     */
    private static String removeSpaces(String src) {
        StringBuffer buffer = new StringBuffer(src.length());

        for (int i = 0; i < src.length(); ++i) {
            char c = src.charAt(i);
            if (!Builder.isWhiteSpace(c))
                buffer.append(c);
        }

        return new String(buffer);
    }

    /**
     * Builds the parse trees.
     * 
     * @return Object[3] first item are TreeNodes[], second String[]
     *         representing provisions and the third String representing the
     *         unbound operations
     * @throws SyntaxErrorException
     */
     static Object[] buildTrees(String[] inputdata, ActionRepository repository) throws SyntaxErrorException {
        TreeNode[] nodes;
        String[] provisions;
        String unbound;

        ArrayList prots = new ArrayList();
        ArrayList provs = new ArrayList();
        Builder parser = new Builder(repository);

        ProtocolReader reader;

        if (Options.inputfile.equals("")) {
            int i;
            int j;
            nodes = new TreeNode[inputdata.length / 2];
            provisions = new String[inputdata.length / 2 - 1];

            for (i = 0, j = 0; i < inputdata.length / 2 - 1; ++i) {
                nodes[i] = parser.build(new StringTokenizer(inputdata[j++]), false);
                provisions[i] = removeSpaces(inputdata[j++]);
            }

            // the last protocol
            nodes[i] = parser.build(new StringTokenizer(inputdata[j++]), false);

            // the last argument is a list of methods of unbound interfaces,
            // coma separated
            unbound = removeSpaces(inputdata[j++]);

            Object[] result = new Object[3];

            result[0] = nodes;
            result[1] = provisions;
            result[2] = unbound;

            return result;

        } else {
            // the input data are in a file
            try {
                reader = new FileTokenizer(Options.inputfile);
            } catch (IOException e) {
                throw new SyntaxErrorException("Cannot read the source file");
            }

            while (true) {
                reader.resetIndex();
                TreeNode node = parser.build(reader, false);
                if (node != null) {
                    prots.add(node);
                    provs.add(parseSynchro(reader));
                } else
                    break;
            }
            
            try {
                reader.close();
            }
            catch (IOException e) {
                Debug.println("Warning: unable to close input file");
            }

            nodes = new TreeNode[prots.size()];
            provisions = new String[provs.size() - 1];
            unbound = removeSpaces((String) provs.get(provs.size() - 1));

            for (int i = 0; i < prots.size(); ++i)
                nodes[i] = (TreeNode) prots.get(i);

            for (int i = 0; i < provs.size() - 1; ++i)
                provisions[i] = (String) provs.get(i);

            Object[] result = new Object[3];

            result[0] = nodes;
            result[1] = provisions;
            result[2] = unbound;

            return result;
        }

    }

     /**
      * Parses the synchro operations
      * @param reader the reader to be used for reading input data
      * @return string with synchro operations
      * @throws SyntaxErrorException in a case syntax error occurs.
      */
    private static String parseSynchro(ProtocolReader reader) throws SyntaxErrorException {
        String result = new String();
        char c;
        
        try {
	        while ((c = (char)reader.read()) != (char)-1)
	            result += c;
	        
	        Debug.println("Synchroops: " + result);
	        return result;
        }
        catch (IOException e) {
            throw new SyntaxErrorException("Error while reading the input file");
        }
    }

    /** @link dependency */
    /* # DFSRChecker lnkDFSRChecker; */

    /** @link dependency */
    /* # DotVisBackend lnkDotVisBackend; */
}
